import { Component, Injector, Input, NgZone, OnInit, ViewChild } from '@angular/core';
import { DesignerEnvType, DomService, FormBasicService, FormSchemaEntityField$Type, SchemaService } from '@farris/designer-services';
import { RowNode, TreeTableComponent } from '@farris/ui-treetable';
import { IdeTreeComponent } from '../ide-tree/ide-tree.component';
import { RefreshFormAfterSchemaChangedService } from '../update-schema/refresh-after-schema-changed.service';
import { UpdateFormSchemaService } from '../update-schema/update-schema.service';
import { SchemaTreeNode } from './entity/schema-tree-node';

@Component({
    selector: 'schema-tree',
    templateUrl: './schema-tree.component.html',
    styleUrls: ['./schema-tree.component.css'],
})
export class SchemaTreeComponent implements OnInit {

    /** 可视化区域构造器 */
    @Input() designerBuilder: any;
    /** 是否启用刷新 */
    @Input() enableRefresh = true;

    /** 实体树绑定数据 */
    public treeData: SchemaTreeNode[] = [];

    /** IDE实体树组件实例 */
    @ViewChild(IdeTreeComponent) private ideTree: IdeTreeComponent;

    leafIcon = ' f-icon f-icon-yxs_level text-primary mr-1';

    constructor(
        private schemaServ: SchemaService,
        private ngZone: NgZone,
        private injector: Injector,
        private domServ: DomService,
        private formBasicService: FormBasicService) { }

    ngOnInit() {
        this.addSchemaTreeBodyToDragulaContainer();

        if (this.formBasicService && this.formBasicService.envType === DesignerEnvType.designer) {
            this.leafIcon = ' f-icon f-icon-preview mr-1';
        }
    }


    /**
     * 组装schema实体的树数据
     */
    refreshTreeData() {
        const schemaEntity = this.schemaServ.getSchemaEntities();
        const rootData = this.assembleSchemEntityTree(schemaEntity[0]) as any;
        rootData.isMainEntity = true;
        if (this.treeData.length) {
            this.assignExpandState(rootData, this.treeData[0]);
        }
        this.treeData = [rootData];



    }

    /**
     * 更新拖拽列表中的实体树节点
     */
    updateTreeNodesInDragula() {
        const tbody = this.ideTree.tt.el.nativeElement.querySelector('tbody.farris-treetable-tbody');
        if (tbody) {
            this.addTreeBodyToDragula(tbody);
        }
    }
    /**
     * 组装实体树数据
     */
    private assembleSchemEntityTree(schemaEntity: any, entityPath: string = '/') {
        const displayedFieldsMap = this.getViewModelFieldsByEntity(entityPath);

        let fieldsTree = [];
        if (schemaEntity.type && schemaEntity.type.fields && schemaEntity.type.fields.length > 0) {
            fieldsTree = this.schemaServ.assembleFields2Tree(schemaEntity.type.fields, false, displayedFieldsMap);
        }

        const entityTree = {
            data: schemaEntity,
            children: fieldsTree,
            expanded: true,
            nodeType: 'entity',
            entityPath
        };
        const childTree = [];
        if (schemaEntity.type.entities && schemaEntity.type.entities.length > 0) {
            for (const en of schemaEntity.type.entities) {
                const childEntityPath = `${entityPath === '/' ? '' : entityPath}/${en.label}`;
                const childEntity = this.assembleSchemEntityTree(en, childEntityPath);
                if (childEntity) {
                    childTree.push(childEntity);
                }
            }
            entityTree.children.push(
                {
                    data: { id: 'childEntity_' + schemaEntity.id, name: '子表' },
                    children: childTree,
                    expanded: true,
                    nodeType: 'childEntity'
                }
            );
        }

        return entityTree;
    }

    /**
     * 刷新实体树时复制树展开状态
     * @param newData 新的treeData
     * @param oldData 旧的treeData
     */
    private assignExpandState(newData, oldData) {
        // 展平oldData，放入map
        const oldTreeNodeMap = new Map<string, any>();
        const queue = [oldData];
        while (queue.length) {
            const current = queue.shift();
            oldTreeNodeMap.set(current.id, current);
            if (current.children && current.children.length) {
                queue.push(...current.children);
            }
        }

        // 队列处理newData
        queue.push(newData);
        while (queue.length) {
            const current = queue.shift();
            const id = current.data && current.data.id;
            if (oldTreeNodeMap.has(id)) {
                const oldNode = oldTreeNodeMap.get(id);
                current.expanded = oldNode.expanded;
            }
            if (current.children && current.children.length) {
                queue.push(...current.children);
            }
        }
    }


    /**
     * 实体树加载完成后将tbody节点添加到可拖拽列表中
     */
    private addSchemaTreeBodyToDragulaContainer() {

        this.ideTree.loadSuccess.subscribe((data: { tt: TreeTableComponent, tbody: HTMLElement }) => {
            this.addTreeBodyToDragula(data.tbody);
        });
    }

    /**
     * 将实体书tbody加到dragula的拖拽列表中
     * @param tbodyDom 树控件tbody元素节点
     */
    private addTreeBodyToDragula(tbodyDom: HTMLElement) {
        if (!this.designerBuilder) {
            return;
        }
        if (this.designerBuilder.instance.dragula) {

            this.designerBuilder.instance.dragula.containers =
                this.designerBuilder.instance.dragula.containers.filter(e => !e.className.includes('farris-treetable-tbody'));

            // tbody节点不接收拖拽进来的内容
            if (!tbodyDom.className.includes('no-drop')) {
                tbodyDom.className += ' no-drop';
            }
            // 配置tr节点拖拽相关属性
            if (tbodyDom.children && tbodyDom.children.length) {
                this.addDragClassToTrElement(tbodyDom);
            }
            // 添加到拖拽列表
            this.designerBuilder.instance.dragula.containers.push(tbodyDom);
        }
    }

    /**
     * 树节点展开后，重新收集tbody容器
     * @param $event $event
     */
    onTreeNodeExpand($event) {
        const tbodyDom = $event.tbody;
        // 点击节点前的展开图标时，需要等待子级tr渲染完成后，再收集容器
        if ($event.node) {
            this.ngZone.runOutsideAngular(() => {
                setTimeout(() => this.addTreeBodyToDragula(tbodyDom));
            });
        } else {
            // 点击搜索栏的展开按钮
            this.addTreeBodyToDragula(tbodyDom);
        }
    }

    /**
     * 配置tr节点拖拽相关属性
     * @param tbodyDom 实体树tbody节点
     */
    private addDragClassToTrElement(tbodyDom: HTMLElement) {
        Array.from(tbodyDom.children).forEach(trEle => {
            const nodeId = trEle.getAttribute('id');
            const rowNode = this.getRowNodeById(nodeId);
            if (!rowNode) {
                if (!trEle.className.includes('no-drag')) {
                    trEle.className += ' no-drag';
                }
            } else {
                this.setDragContextToElement(rowNode, trEle);
            }


        });
    }
    /**
     * 根据id判断当前行是实体节点 or 字段节点
     */
    private getRowNodeById(nodeId: string) {
        if (!nodeId || !nodeId.includes('rownode-') || nodeId.includes('rownode-childEntity')) {
            return;
        }
        const id = nodeId.replace('rownode-', '');
        const rowNode = this.ideTree.tt.findRowNode(id);

        return rowNode;

    }

    private setDragContextToElement(rowNode: RowNode, trEle: Element) {
        const treeNode = rowNode.node as SchemaTreeNode;
        switch (treeNode.data.$type) {
            // 简单字段节点
            case FormSchemaEntityField$Type.SimpleField: {
                trEle.setAttribute('data-sourceType', 'field');
                trEle.setAttribute('data-fieldId', rowNode.node.data.id);
                trEle.setAttribute('data-category', 'input');

                if (!trEle.className.includes('drag-copy')) {
                    trEle.className += ' drag-copy';
                }
                break;
            }
            // udt、关联字段节点
            case FormSchemaEntityField$Type.ComplexField: {
                if (!trEle.className.includes('no-drag')) {
                    trEle.className += ' no-drag';
                }
                break;
            }
            default: {
                // 实体节点
                if (treeNode.nodeType === 'entity') {
                    trEle.setAttribute('data-sourceType', 'entity');
                    trEle.setAttribute('data-fieldId', rowNode.node.data.id);
                    trEle.setAttribute('data-category', 'dataCollection');

                    if (!trEle.className.includes('drag-copy')) {
                        trEle.className += ' drag-copy';
                    }
                }
            }
        }

    }
    /**
     * 更新schema
     */
    onUpdateSchema() {
        const refreshAfterChangedServ = new RefreshFormAfterSchemaChangedService(
            this.injector
        );

        const updateSchemaService = new UpdateFormSchemaService(this.injector);
        updateSchemaService.synchronizeVO(refreshAfterChangedServ);
    }

    /**
     * 行颜色
     */
    rowCls = (row, data, index, rowNode) => {
        const nodeData = rowNode.node;
        if (!nodeData) {
            return;
        }

        if (this.formBasicService && this.formBasicService.envType === DesignerEnvType.designer) {
            return {
                cls: nodeData.isUsed ? 'used' : '!used'
            };
        }

    }
    /**
     * 获取指定实体下，在页面上展示的字段列表
     */
    private getViewModelFieldsByEntity(entityPath: string): Map<string, boolean> {
        if (!this.domServ) {
            return;
        }
        const viewModels = this.domServ.viewmodels;
        const fieldsMap = new Map<string, boolean>();
        /** 记录每个字段出现了多少次。 */
        const fieldsCountingMap = new Map<string, number>();

        // 可能多个视图模型绑定同一个实体
        const matchingViewModels = viewModels.filter(viewModel => viewModel.bindTo === entityPath);
        matchingViewModels.forEach(viewModel => {
            if (viewModel.fakeDel) {
                return;
            }
            const cmp = this.domServ.getComponentByVMId(viewModel.id);
            if (!cmp || cmp.fakeDel) {
                return;
            }
            if (!viewModel.fields) {
                return;
            }
            viewModel.fields.forEach(field => {
                const count = fieldsCountingMap.has(field.id) ? fieldsCountingMap.get(field.id) + 1 : 1;
                fieldsCountingMap.set(field.id, count);
            });
        });
        fieldsCountingMap.forEach((count, id) => {
            fieldsMap.set(id, true);
        });
        return fieldsMap;
    }

}
